home *** CD-ROM | disk | FTP | other *** search
Text File | 1995-12-11 | 21.8 KB | 798 lines | [TEXT/MPS ] |
- /*
- File: CappuccinoDragDrop.cpp
-
- Contents: Implements drag & drop support.
-
- Written by: Troy Gaul
-
- Copyright: © 1995 by Apple Computer, Inc., all rights reserved.
- */
-
- // -- Compiler/Preprocessor Switches --
-
- #ifndef _COMPILERDEFS_
- #include "CompDefs.h"
- #endif
-
- // -- OpenDoc Utilities --
-
- #ifndef _EXCEPT_
- // Exceptions define several important macros (eg. CHECKENV)
- // which are used in the SOM method dispatch glue. If Except.h
- // is not included early enough, exceptions may not be thrown
- // correctly when returning from a SOM method with the "ev" parameter set.
- #include <Except.h>
- #endif
-
- // -- Cappuccino Includes --
-
- #ifndef _CAPPUCCINO_
- #include "Cappuccino.h"
- #endif
-
- #ifndef _CAPPUCCINOACTION_
- #include "CappuccinoAction.h"
- #endif
-
- #ifndef _CAPPUCCINOCONTENT_
- #include "CappuccinoContent.h"
- #endif
-
- #ifndef _CAPPUCCINODEF_
- #include "CappuccinoDef.h"
- #endif
-
- #ifndef _CAPPUCCINOGLOBALS_
- #include "CappuccinoGlobals.h"
- #endif
-
- #ifndef _CAPPUCCINOPROMISE_
- #include "CappuccinoPromise.h"
- #endif
-
- // -- OpenDoc Includes --
-
- #ifndef SOM_ODDragItemIterator_xh
- #include <DgItmIt.xh>
- #endif
-
- #ifndef SOM_ODDragAndDrop_xh
- #include <DragDrp.xh>
- #endif
-
- #ifndef SOM_ODFacet_xh
- #include <Facet.xh>
- #endif
-
- #ifndef SOM_ODFrame_xh
- #include <Frame.xh>
- #endif
-
- #ifndef _ODDEBUG_
- #include <ODDebug.h>
- #endif
-
- #ifndef _ODTYPES_
- #include <ODTypes.h>
- #endif
-
- #ifndef SOM_ODSession_xh
- #include <ODSessn.xh>
- #endif
-
- #ifndef SOM_ODShape_xh
- #include <Shape.xh>
- #endif
-
- #ifndef SOM_Module_OpenDoc_StdProps_defined
- #include <StdProps.xh>
- #endif
-
- #ifndef SOM_Module_OpenDoc_StdTypes_defined
- #include <StdTypes.xh>
- #endif
-
- #ifndef SOM_ODStorageUnit_xh
- #include <StorageU.xh>
- #endif
-
- #ifndef SOM_ODTranslation_xh
- #include <Translt.xh>
- #endif
-
- #ifndef SOM_ODUndo_xh
- #include <Undo.xh>
- #endif
-
- // -- OpenDoc Utilities --
-
- #ifndef _BARRAY_
- #include <BArray.h>
- #endif
-
- #ifndef _FOCUSLIB_
- #include <FocusLib.h>
- #endif
-
- #ifndef _ODUTILS_
- #include <ODUtils.h>
- #endif
-
- #ifndef _STDTYPIO_
- #include <StdTypIO.h>
- #endif
-
- #ifndef _STORUTIL_
- #include <StorUtil.h>
- #endif
-
- #ifndef _TEMPOBJ_
- #include <TempObj.h>
- #endif
-
- //==============================================================================
- #pragma mark • ODPart overrides •
- //==============================================================================
-
- //------------------------------------------------------------------------------
- // Method: FulfillPromise
- // Origin: ODPart
- //
- // Description: This method is called when a promise we have given for a
- // storage unit needs to be fulfilled with the actual data.
- //------------------------------------------------------------------------------
-
- void Cappuccino::FulfillPromise( Environment* ev,
- ODStorageUnitView* promiseSUView )
- {
- SOM_Trace("Cappuccino","FulfillPromise");
-
- CPromise* promise = CPromise::GetPromiseFromSUView(ev, promiseSUView);
- THROW_IF_NULL(promise);
-
- promise->Fulfill(ev, promiseSUView);
- }
-
- //------------------------------------------------------------------------------
- // Method: DropCompleted
- // Origin: ODPart
- //
- // Description: This method is called when an asynchronous drag, initiated by
- // this part, has completed.
- //
- // Note: OpenDoc version 1.0 for the Macintosh does not support
- // asynchronous dragging, so this routine will never be called.
- // It is included here so it can be filled in in case it is
- // supported in the future.
- //------------------------------------------------------------------------------
-
- void Cappuccino::DropCompleted( Environment* ev,
- ODPart* destPart,
- ODDropResult dropResult )
- {
- SOM_Trace("Cappuccino","DropCompleted");
- }
-
- //------------------------------------------------------------------------------
- // Method: DragEnter
- // Origin: ODPart
- //
- // Description: This method is called when a drag enters a facet that belongs
- // to this part. We should check to see if we can accept the
- // drag and highlight the facet if so.
- //------------------------------------------------------------------------------
-
- ODDragResult Cappuccino::DragEnter( Environment* ev,
- ODDragItemIterator* dragInfo,
- ODFacet* facet,
- ODPoint* where )
- {
- SOM_Trace("Cappuccino","DragEnter");
-
- #ifndef qViewerBuild
- ODDragResult acceptable = this->IsDragSupported(ev, dragInfo, facet);
-
- if ( acceptable )
- {
- // We can accept everything, so highlight the facet.
-
- ODDragAndDrop* dad = fSession->GetDragAndDrop(ev);
- ODULong dragAttributes = dad->GetDragAttributes(ev);
-
- if (!(dragAttributes & kODDragIsInSourceFrame))
- {
- this->HighlightFacet(ev, facet, kODTrue);
- }
- }
-
- return acceptable;
- #else
- return kODFalse;
- #endif
- }
-
- //------------------------------------------------------------------------------
- // Method: DragWithin
- // Origin: ODPart
- //
- // Description: This method is called while a drag is still within a facet
- // belonging to this part. Since we already highlighted it during
- // DragEnter, we don't need to do anything here. If we had
- // multiple drag areas, we could track them in this routine.
- //------------------------------------------------------------------------------
-
- ODDragResult Cappuccino::DragWithin( Environment* ev,
- ODDragItemIterator* dragInfo,
- ODFacet* facet,
- ODPoint* where )
- {
- SOM_Trace("Cappuccino","DragWithin");
-
- #ifndef qViewerBuild
- return kODTrue;
- #else
- return kODFalse;
- #endif
- }
-
- //------------------------------------------------------------------------------
- // Method: DragLeave
- // Origin: ODPart
- //
- // Description: This method is called when a drag leaves a facet belonging to
- // this part after it had previously entered it. We must remove
- // our drag highlighting at this point.
- //------------------------------------------------------------------------------
-
- void Cappuccino::DragLeave( Environment* ev,
- ODFacet* facet,
- ODPoint* where)
- {
- SOM_Trace("Cappuccino","DragLeave");
-
- #ifndef qViewerBuild
- this->HighlightFacet(ev, facet, kODFalse);
- #endif
- }
-
- //------------------------------------------------------------------------------
- // Method: Drop
- // Origin: ODPart
- //
- // Description: This method is called when a drag has resulted in dropping
- // the content onto a facet belonging to this part. We must
- // remove the drag highlighting and handle the drop here.
- //------------------------------------------------------------------------------
-
- ODDropResult Cappuccino::Drop( Environment* ev,
- ODDragItemIterator* dropInfo,
- ODFacet* facet,
- ODPoint* where )
- {
- SOM_Trace("Cappuccino","Drop");
-
- #ifndef qViewerBuild
- ODDropResult result = kODDropFail;
-
- // Remove the drag highlighting.
- this->HighlightFacet(ev, facet, kODFalse);
-
- ODULong dragAttributes = fSession->GetDragAndDrop(ev)->GetDragAttributes(ev);
-
- if ( dragAttributes & kODDropIsInSourcePart )
- {
- result = kODDropFail;
- }
- else if ( this->IsDragSupported(ev, dropInfo, facet) )
- {
- if ( !this->TryToEdit(ev, facet->GetFrame(ev)) )
- return kODDropFail;
-
- // Inspect the type of data being dragged to see if we can accept it.
- for ( ODStorageUnit* dropSU = dropInfo->First(ev) ; dropSU ;
- dropSU = dropInfo->Next(ev) )
- {
- if ( CCappuccinoContent::HasValidContent(ev, dropSU) )
- {
- result = (dragAttributes & kODDropIsMove) ? kODDropMove
- : kODDropCopy;
-
- // Determine the nature of the drag (copy vs. move).
- if ( dragAttributes & kODDropIsPasteAs )
- {
- ODBoolean pasteLink;
- this->DoDropPasteAs(ev, facet, &pasteLink);
-
- // We don't currently support linking, but we'll check
- // this flag and set the result appropriately so that
- // when support is added, we're handling it correctly.
- if ( pasteLink )
- {
- // If we're pasting a link, we have to make sure it's
- // a copy. A move is not possible, because it would
- // mean that we'd be removing the source of the link.
- result = kODDropCopy;
- }
- }
- else
- {
- CCappuccinoContent* content = new CCappuccinoContent(this);
- TempRefCounted contentTemp = content;
- content->InitCappuccinoContent(ev, dropSU);
-
- CDropAction* action = new CDropAction(this, content);
- action->Perform(ev);
- }
- }
- }
- }
-
- return result;
- #else
- return kODDropFail;
- #endif
- }
-
- //==============================================================================
- #pragma mark • Cappuccino functions •
- //==============================================================================
-
- //------------------------------------------------------------------------------
- // Method: HighlightFacet
- // Origin: Cappuccino
- //
- // Description: This method is called when a facet of this part needs to be
- // highlighted because it is able to accept a drop.
- //------------------------------------------------------------------------------
-
- void Cappuccino::HighlightFacet( Environment* ev,
- ODFacet* facet,
- ODBoolean show)
- {
- #ifndef qViewerBuild
- ODDragAndDrop* dad = fSession->GetDragAndDrop(ev);
-
- CFocus forDrawing(ev, facet);
-
- if ( show )
- {
- ODFrame* displayFrame = facet->GetFrame(ev);
- TempODShape frameShape = displayFrame->AcquireFrameShape(ev, kODNULL);
- RgnHandle bRgn = frameShape->GetQDRegion(ev);
-
- ShowDragHilite(dad->GetDragReference(ev), bRgn, true);
-
- fHighlightedDragFacet = facet;
- }
- else
- {
- if ( fHighlightedDragFacet != kODNULL )
- {
- HideDragHilite(dad->GetDragReference(ev));
- fHighlightedDragFacet = kODNULL;
- }
- }
- #endif
- }
-
- //------------------------------------------------------------------------------
- // Method: IsDragSupported
- // Origin: Cappuccino
- //
- // Description: This method is called when the part needs to know if the
- // contents of a particular kind of drag are supported.
- //
- // Notes: For a drag to be supported, the contents of all of the drag
- // items must be supported.
- //
- // In addition, Cappuccino only supports one piece of content, and
- // thus can only accept a drag of one item (two or more items will
- // always be rejected, regardless of content).
- //------------------------------------------------------------------------------
-
- ODBoolean Cappuccino::IsDragSupported( Environment* ev,
- ODDragItemIterator* dragInfo,
- ODFacet* facet )
- {
- SOM_Trace("Cappuccino","DragEnter");
-
- #ifndef qViewerBuild
- ODBoolean isSupported = kODFalse;
-
- if ( !fReadOnlyStorage &&
- (facet->GetFrame(ev)->GetLinkStatus(ev) != kODInLinkDestination) )
- {
- isSupported = kODTrue;
- ODULong count = 0;
-
- // Inspect the type of data being dragged to see if we can accept it.
- for ( ODStorageUnit* dragSU = dragInfo->First(ev) ; dragSU ;
- dragSU = dragInfo->Next(ev) )
- {
- // The recipe says that in order to accept a drag, we have to be
- // able to accept ALL of the contents of the drag. Since we only
- // have one string, the most that we can handle is one item.
- if ( ++count > 1 )
- {
- isSupported = kODFalse;
- break;
- }
-
- // Determine if we support the drag content and if it would
- // need to be translated.
- ODBoolean itemHasValidData = CCappuccinoContent::HasValidContent(ev, dragSU);
-
- // We have to be able to accept everything dropped on us, so see
- // if this item is acceptable, and if not, reject the entire drag.
- if ( !itemHasValidData )
- {
- isSupported = kODFalse;
- break;
- }
- }
-
- }
-
- return isSupported;
- #else
- return kODFalse;
- #endif
- }
-
- //------------------------------------------------------------------------------
- // Method: InitiateDrag
- // Origin: Cappuccino
- //
- // Description: This method is called when the user clicks the mouse in the
- // part and starts dragging, causing it to need to start dragging
- // its content around.
- //
- // This method puts promises to the part's content into the drag
- // and drop storage unit, calls OpenDoc to perform the drag, and
- // then handles the result of the drag.
- //------------------------------------------------------------------------------
-
- ODDropResult Cappuccino::InitiateDrag( Environment* ev,
- ODEventData* event,
- ODFacet* facet,
- ODPoint& where)
- {
- SOM_Trace("Cappuccino","InitateDrag");
-
- ODBoolean canChange = !fReadOnlyStorage &&
- (facet->GetFrame(ev)->GetLinkStatus(ev) != kODInLinkDestination);
-
- ODDropResult dropResult = kODDropFail;
-
- // Get the ODDragAndDrop object from the session.
- ODDragAndDrop* dragAndDrop = fSession->GetDragAndDrop(ev);
-
- // Reinitialize the ODDragAndDrop object.
- dragAndDrop->Clear(ev);
-
- // Get the Storage Unit where data for dragged objects are going to be written.
- ODStorageUnit* su = dragAndDrop->GetContentStorageUnit(ev);
-
- // Write out the data.
- this->CloneContents(ev, su, canChange ? kODCloneCut : kODCloneCopy,
- facet->GetFrame(ev), kIsNotForClipboard);
-
- // Write out the mouse down offset.
- ODSetPointProp(ev, su, kODPropMouseDownOffset, kODPoint, &where);
-
- // Initiate the drag.
- TempODPart destPart = kODNULL;
-
- // Add a begin token to the undo stack to wrap any undo actions that are
- // added in the course of the drag (since the drag should be perceived by
- // the user as a single operation).
- CDragBeginAction* beginAction = new CDragBeginAction(this);
- beginAction->Perform(ev);
-
- TRY
- // Get the drag region.
- ODRgnHandle dragRgn = this->CreateDragRegion(ev, facet);
-
- TRY
- // Create byte arrays for dragRn and refCon (which is the event record).
- TempODByteArray dragRgnBA = CreateByteArray(&dragRgn, sizeof(RgnHandle));
- TempODByteArray eventBA = CreateByteArray(&event, sizeof(ODEventData*)); // event is of type ODEventData*.
-
- // Here we have an ODPart that is used in order to store the
- // result of the drag (its destination). We can't use destPart
- // for this purpose because it is a TempRef, and we need to be
- // able to take the address of the ODPart pointer to pass into
- // StartDrag.
- ODPart* theDestPart = kODNULL;
-
- dropResult = dragAndDrop->StartDrag(ev, facet->GetFrame(ev),
- kODDragImageRegionHandle,
- dragRgnBA, &theDestPart, eventBA);
-
- // Assign the resulting part to our temp object so that it
- // will be released automatically when it goes out of scope.
- destPart = theDestPart;
- CATCH_ALL
- DisposeRgn(dragRgn);
- RERAISE;
- ENDTRY
-
- DisposeRgn(dragRgn);
-
- // Handle the result of the drag.
- if ( dropResult == kODDropCopy || dropResult == kODDropMove )
- {
- if ( ODObjectsAreEqual(ev, destPart, fSelf) )
- {
- // Dragged back into ourself, ignore.
- }
- else if ( dropResult == kODDropMove )
- {
- #ifndef qViewerBuild
- // It was a move, so we need to remove it from our content.
- if ( canChange )
- this->DoClear(ev, facet->GetFrame(ev));
- #endif
- }
- }
- CATCH_ALL
- fSession->GetUndo(ev)->AbortCurrentTransaction(ev);
- gGlobals->fDragPromises->Clear(ev);
- RERAISE;
- ENDTRY
-
- gGlobals->fDragPromises->Clear(ev);
-
- // Finish off the undo action.
- if ( dropResult == kODDropFail )
- {
- fSession->GetUndo(ev)->AbortCurrentTransaction(ev);
- }
- else
- {
- // Add an end token to the undo stack to wrap any undo actions that are
- // added in the course of the drag.
- CDragEndAction* endAction = new CDragEndAction(this);
- endAction->Perform(ev);
- }
-
- return dropResult;
- }
-
- //------------------------------------------------------------------------------
- // Method: CreateDragRegion
- // Origin: Cappuccino
- //
- // Description: This method is called when we need to create a region handle
- // that represents the area to be dragged for a drag and drop.
- //------------------------------------------------------------------------------
-
- ODRgnHandle Cappuccino::CreateDragRegion( Environment* ev,
- ODFacet* facet )
- {
- ASSERT_NOT_NULL(facet);
-
- ODRgnHandle dragRgn = ODNewRgn();
-
- TRY
- ODFrame* frame = facet->GetFrame(ev);
- TempODShape frameShape = kODNULL;
-
- if ( frame->IsRoot(ev) )
- {
- // If we're at the root, we need to use the bounds of our shape so
- // that the cutout corner (for the grow box) isn't included in the
- // shape.
- frameShape = ODCopyAndRelease(ev, frame->AcquireFrameShape(ev, kODNULL));
-
- ODRect bounds;
- frameShape->GetBoundingBox(ev, &bounds);
- frameShape->SetRectangle(ev, &bounds);
- }
- else
- {
- frameShape = frame->AcquireFrameShape(ev, kODNULL);
- }
-
- // Get the region from the shape.
- CopyRgn(frameShape->GetQDRegion(ev), dragRgn);
-
- // Inset the region and subtract it from the original. This is
- // done to get a region that just contains the frame of the area
- // and isn't filled in.
- {
- ODRgnHandle insetRgn = ODNewRgn();
-
- CopyRgn(dragRgn, insetRgn);
- InsetRgn(insetRgn, 1, 1);
- DiffRgn(dragRgn, insetRgn, dragRgn);
-
- ODDisposeHandle((ODHandle) insetRgn);
- }
-
- // Put the region in global coordinates.
- {
- CFocus forMapping(ev, facet);
-
- Point localToGlobalTrans = { 0, 0 };
- LocalToGlobal(&localToGlobalTrans);
- OffsetRgn(dragRgn, localToGlobalTrans.h, localToGlobalTrans.v);
- }
- CATCH_ALL
- ODDisposeHandle((ODHandle) dragRgn);
- RERAISE;
- ENDTRY
-
- return dragRgn;
- }
-
- //------------------------------------------------------------------------------
- // Method: DoDropPasteAs
- // Origin: Cappuccino
- //
- // Description: This method is called by the part when the user uses the
- // approprate drag-and-drop modifier keys.
- //------------------------------------------------------------------------------
-
- void Cappuccino::DoDropPasteAs( Environment* ev,
- ODFacet* facet,
- ODBoolean* pasteLink )
- {
- SOM_Trace("Cappuccino","DoPaste");
-
- #ifndef qViewerBuild
- ASSERT_NOT_NULL(facet);
-
- ODDragAndDrop* dad = fSession->GetDragAndDrop(ev);
- ODStorageUnit* contentSU = dad->GetContentStorageUnit(ev);
-
- ODPasteAsResult pasteAsResult;
- pasteAsResult.selectedKind = kODNULL;
- pasteAsResult.translateKind = kODNULL;
- pasteAsResult.editor = kODNULL;
- TempODPasteAsResult pasteAsResultTemp = &pasteAsResult;
-
- // Parameters except pasteAsResult are omitted
- if ( dad->ShowPasteAsDialog(ev,
- kODTrue, // canPasteLink
- kODPasteAsMergeOnly, // mergeSetting
- facet,
- kODNullTypeToken, // viewtype
- contentSU,
- &pasteAsResult) )
- {
- this->HandlePasteAsResult(ev, pasteAsResult, contentSU);
- }
-
- // If we're pasting it as a link, inform the caller.
- *pasteLink = pasteAsResult.pasteLinkSetting;
-
- #endif
- }
-
- //==============================================================================
- #pragma mark • Data Interchange •
- //==============================================================================
-
- //------------------------------------------------------------------------------
- // Method: AnnotateInterchangeSU
- // Origin: Cappuccino
- //
- // Description: This method is called by the part when it is putting data onto
- // the clipboard or drag-and-drop storage unit. It adds an
- // annotation that indicates the suggested frame shape based on
- // the current frame shape.
- //------------------------------------------------------------------------------
-
- void Cappuccino::AnnotateInterchangeSU( Environment* ev,
- ODStorageUnit* su,
- ODFrame* frame )
- {
- SOM_Trace("Cappuccino","AnnotateInterchangeSU");
-
- ODSUForceFocus(ev, su, kODPropSuggestedFrameShape, kODNULL);
-
- TempODShape frameShape = kODNULL;
-
- if ( frame->IsRoot(ev) )
- {
- // If we're at the root, we need to use the bounds of our shape so
- // that the cutout corner (for the grow box) isn't included in the
- // shape that we write out.
- frameShape = ODCopyAndRelease(ev, frame->AcquireFrameShape(ev, kODNULL));
-
- ODRect bounds;
- frameShape->GetBoundingBox(ev, &bounds);
- frameShape->SetRectangle(ev, &bounds);
- }
- else
- {
- frameShape = frame->AcquireFrameShape(ev, kODNULL);
- }
- frameShape->WriteShape(ev, su);
- }
-
- //------------------------------------------------------------------------------
- // Method: CloneContents
- // Origin: Cappuccino
- //
- // Description: This method is called by the part when the user chooses the
- // Copy/Cut menu item or starts a drag. It promises the part's
- // content to the given storage unit.
- //------------------------------------------------------------------------------
-
- void Cappuccino::CloneContents( Environment* ev,
- ODStorageUnit* su,
- ODCloneKind cloneKind,
- ODFrame* scopeFrame,
- ODBoolean isForClipboard )
- {
- SOM_Trace("Cappuccino","CloneContents");
-
- ODDraft* fromDraft = ODGetDraft(ev, fSelf);
- ODDraft* toDraft = su->GetDraft(ev);
- ODDraftKey key = kODNULLKey;
-
- ODVolatile(key);
-
- TRY
- key = fromDraft->BeginClone(ev, toDraft, kODNULL, cloneKind);
-
- // Verify that the properties we want to write into exist.
- this->CheckAndAddProperties(ev, su);
-
- // Write out the part's content annotation.
- this->ExternalizeStateInfo(ev, su, key, scopeFrame);
-
- WASSERT(fContent != kODNULL);
-
- fContent->Promise(ev, su, key, isForClipboard, this);
-
- fromDraft->EndClone(ev, key);
- CATCH_ALL
- if ( key != kODNULLKey )
- fromDraft->AbortClone(ev, key);
- RERAISE;
- ENDTRY
-
- this->AnnotateInterchangeSU(ev, su, scopeFrame);
- }
-
- //------------------------------------------------------------------------------
- // Method: HandlePasteAsResult
- // Origin: Cappuccino
- //
- // Description: This method is called by the part when the the Paste As dialog
- // (from either the Paste As command or from a drag-and-drop has
- // been dismissed by clicking on the OK button.
- //------------------------------------------------------------------------------
-
- void Cappuccino::HandlePasteAsResult( Environment* ev,
- ODPasteAsResult& pasteAsResult,
- ODStorageUnit* contentSU )
- {
- SOM_Trace("Cappuccino","DoPaste");
-
- #ifndef qViewerBuild
- ASSERT_NOT_NULL(contentSU);
-
- CCappuccinoContent* content = new CCappuccinoContent(this);
- TempRefCounted contentTemp = content;
-
- if ( pasteAsResult.translateKind != kODNULL )
- {
- content->InitByTranslating(ev, contentSU, pasteAsResult.translateKind,
- pasteAsResult.selectedKind);
- }
- else
- {
- content->InitCappuccinoContent(ev, contentSU);
- }
-
- // Create an action to paste the new content.
- CPasteAction* action = new CPasteAction(this, content);
- action->Perform(ev);
- #endif
- }
-
-